#include "./BSP/PWMDAC/pwmdac.h"

TIM_HandleTypeDef g_tim1_handler;     /* 定时器1句柄 */
TIM_OC_InitTypeDef g_tim1_oc_handler; /* 定时器1通道句柄 */

/**
 * @brief      PWM DAC初始化函数, 实际上就是初始化定时器
 * @param[in]  arr         自动重装载值,决定PWM频率
 * @param[in]  psc         时钟预分频数,决定定时器计数频率
 *
 * @note       定时器的时钟来自APB1 / APB2, 当APB1 / APB2 分频时, 定时器频率自动翻倍
 *             定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *             Ft = 定时器工作频率, 单位: Mhz
 *             
 *             GP8101芯片要求PWM频率在50Hz-50KHz范围内，推荐使用20KHz
 *             使用arr=255, psc=31时，PWM频率 = 168MHz/((255+1)*(31+1)) = 20.51KHz
 * @retval     无
 */
void pwmdac_init(uint16_t arr, uint16_t psc)
{
    /* 配置定时器基本参数 */
    g_tim1_handler.Instance = PWMDAC_TIMX;                  /* 定时器1 */
    g_tim1_handler.Init.Prescaler = psc;                    /* 设置预分频器 */
    g_tim1_handler.Init.CounterMode = TIM_COUNTERMODE_UP;   /* 向上计数模式 */
    g_tim1_handler.Init.Period = arr;                       /* 自动重装载值 */
    g_tim1_handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    g_tim1_handler.Init.RepetitionCounter = 0;
    g_tim1_handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    HAL_TIM_PWM_Init(&g_tim1_handler);                      /* 初始化PWM定时器 */

    /* 配置PWM通道参数 */
    g_tim1_oc_handler.OCMode = TIM_OCMODE_PWM1;             /* PWM模式1: CNT<CCR时输出有效电平 */
    g_tim1_oc_handler.Pulse = 0;                            /* 设置比较值为0，初始占空比为0% */
    g_tim1_oc_handler.OCPolarity = TIM_OCPOLARITY_HIGH;     /* 输出极性为高电平有效 */
    g_tim1_oc_handler.OCNPolarity = TIM_OCNPOLARITY_HIGH;   /* 互补输出极性 */
    g_tim1_oc_handler.OCFastMode = TIM_OCFAST_DISABLE;      /* 快速模式禁用 */
    g_tim1_oc_handler.OCIdleState = TIM_OCIDLESTATE_RESET;  /* 空闲状态下的输出电平 */
    g_tim1_oc_handler.OCNIdleState = TIM_OCNIDLESTATE_RESET;/* 空闲状态下的互补输出电平 */

    /* 配置并启动各个通道 */
    HAL_TIM_PWM_ConfigChannel(&g_tim1_handler, &g_tim1_oc_handler, PWMDAC1_TIMX_CHY);    /* 配置TIM1通道4 */
    HAL_TIM_PWM_ConfigChannel(&g_tim1_handler, &g_tim1_oc_handler, PWMDAC2_TIMX_CHY);    /* 配置TIM1通道3 */
    HAL_TIM_PWM_ConfigChannel(&g_tim1_handler, &g_tim1_oc_handler, PWMDAC3_TIMX_CHY);    /* 配置TIM1通道2 */
    HAL_TIM_PWM_ConfigChannel(&g_tim1_handler, &g_tim1_oc_handler, PWMDAC4_TIMX_CHY);    /* 配置TIM1通道1 */

    /* 启动PWM输出 */
    HAL_TIM_PWM_Start(&g_tim1_handler, PWMDAC1_TIMX_CHY);
    HAL_TIM_PWM_Start(&g_tim1_handler, PWMDAC2_TIMX_CHY);
    HAL_TIM_PWM_Start(&g_tim1_handler, PWMDAC3_TIMX_CHY);
    HAL_TIM_PWM_Start(&g_tim1_handler, PWMDAC4_TIMX_CHY);
    
    /* 使能TIM1高级定时器的主输出 */
    HAL_TIMEx_ConfigBreakDeadTime(&g_tim1_handler, &(TIM_BreakDeadTimeConfigTypeDef){0});
    __HAL_TIM_MOE_ENABLE(&g_tim1_handler);
}

/**
 * @brief      定时器PWM底层初始化回调函数
 * @param[in]  htim        定时器句柄指针
 *
 * @note       该函数会被HAL_TIM_PWM_Init()自动调用
 * @retval     无
 */
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef gpio_init_struct;

    if (htim->Instance == PWMDAC_TIMX)
    {
        PWMDAC_TIMX_CLK_ENABLE();           /* 使能TIM1时钟 */
        
        /* 初始化PWMDAC1引脚 */
        PWMDAC1_GPIO_CLK_ENABLE();
        gpio_init_struct.Pin = PWMDAC1_GPIO_PIN;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        gpio_init_struct.Pull = GPIO_PULLUP;
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
        gpio_init_struct.Alternate = PWMDAC1_GPIO_AFTIMX;
        HAL_GPIO_Init(PWMDAC1_GPIO_PORT, &gpio_init_struct);
        
        /* 初始化PWMDAC2引脚 */
        PWMDAC2_GPIO_CLK_ENABLE();
        gpio_init_struct.Pin = PWMDAC2_GPIO_PIN;
        gpio_init_struct.Alternate = PWMDAC2_GPIO_AFTIMX;
        HAL_GPIO_Init(PWMDAC2_GPIO_PORT, &gpio_init_struct);
        
        /* 初始化PWMDAC3引脚 */
        PWMDAC3_GPIO_CLK_ENABLE();
        gpio_init_struct.Pin = PWMDAC3_GPIO_PIN;
        gpio_init_struct.Alternate = PWMDAC3_GPIO_AFTIMX;
        HAL_GPIO_Init(PWMDAC3_GPIO_PORT, &gpio_init_struct);
        
        /* 初始化PWMDAC4引脚 */
        PWMDAC4_GPIO_CLK_ENABLE();
        gpio_init_struct.Pin = PWMDAC4_GPIO_PIN;
        gpio_init_struct.Alternate = PWMDAC4_GPIO_AFTIMX;
        HAL_GPIO_Init(PWMDAC4_GPIO_PORT, &gpio_init_struct);
    }
}

/**
 * @brief      设置PWMDAC输出占空比
 * @param[in]  channel     PWMDAC通道，取值范围:
 *             @arg        PWMDAC_CH1 或 1: PWMDAC1 (TIM1_CH4)
 *             @arg        PWMDAC_CH2 或 2: PWMDAC2 (TIM1_CH3)
 *             @arg        PWMDAC_CH3 或 3: PWMDAC3 (TIM1_CH2)
 *             @arg        PWMDAC_CH4 或 4: PWMDAC4 (TIM1_CH1)
 * @param[in]  duty        占空比值，范围0-100，对应0%-100%
 * @note       GP8101芯片将PWM占空比线性转换为模拟电压：
 *             - 当SEL接地时，输出范围为0-5V
 *             - 当SEL接V5V时，输出范围为0-10V
 *             - 本系统中SEL接V5V，输出范围为0-10V
 *             - 输出电压 = 10V * 占空比
 * @retval     无
 */
void pwmdac_set_duty(uint8_t channel, uint8_t duty)
{
    uint32_t ccr;
    uint32_t tim_channel;
    
    if (duty > 100) duty = 100;  /* 限制最大值为100% */
    
    /* 计算CCR值：占空比 = CCR/(ARR+1)，所以 CCR = 占空比 * (ARR+1) / 100 */
    ccr = ((uint32_t)duty * (g_tim1_handler.Init.Period + 1)) / 100;
    
    /* 根据通道选择定时器通道 */
    switch (channel)
    {
        case PWMDAC_CH1: tim_channel = PWMDAC1_TIMX_CHY; break;
        case PWMDAC_CH2: tim_channel = PWMDAC2_TIMX_CHY; break;
        case PWMDAC_CH3: tim_channel = PWMDAC3_TIMX_CHY; break;
        case PWMDAC_CH4: tim_channel = PWMDAC4_TIMX_CHY; break;
        default: return; /* 通道参数错误，直接返回 */
    }
    
    __HAL_TIM_SET_COMPARE(&g_tim1_handler, tim_channel, ccr);  /* 设置新的占空比 */
}

/**
 * @brief      设置PWMDAC输出电压（通过调整占空比）
 * @param[in]  channel     PWMDAC通道，取值范围:
 *             @arg        PWMDAC_CH1 或 1: PWMDAC1 (TIM1_CH4)
 *             @arg        PWMDAC_CH2 或 2: PWMDAC2 (TIM1_CH3)
 *             @arg        PWMDAC_CH3 或 3: PWMDAC3 (TIM1_CH2)
 *             @arg        PWMDAC_CH4 或 4: PWMDAC4 (TIM1_CH1)
 * @param[in]  vol         目标电压值,取值范围:0~10000 (对应0~10V)
 * @note       该函数通过调节PWM占空比来控制GP8101输出电压:
 *             - 输入电压范围: 0~10000 (单位:mV)
 *             - 实际电压范围: 0~10V (SEL接V5V时)
 *             - GP8101输出电压 = 10V * 占空比
 * @retval     无
 */
void pwmdac_set_voltage(uint8_t channel, uint16_t vol)
{
    uint8_t duty;
    
    if (vol > 10000) vol = 10000;  /* 限制最大值为10000mV */
    
    /* 计算占空比：使用浮点数计算避免精度损失 */
    duty = (uint8_t)((float)vol * 100.0f / 10000.0f + 0.5f); /* 四舍五入 */
    
    pwmdac_set_duty(channel, duty);  /* 设置占空比 */
}

/**
 * @brief      设置PWMDAC输出4~20mA电流
 * @param[in]  channel     PWMDAC通道，取值范围:
 *             @arg        PWMDAC_CH3 或 3: PWMDAC3 (TIM1_CH2)
 *             @arg        PWMDAC_CH4 或 4: PWMDAC4 (TIM1_CH1)
 * @param[in]  curr        目标电流值，单位为μA
 *              范围: 0或4000~20000，对应0或4~20mA
 *              - 当curr=0时，关闭输出(设置为0mA)
 *              - 当0<curr<4000时，自动调整为4000(4mA)
 * @note       此函数用于PWMDAC3和PWMDAC4通道，这两个通道通过GP8101和XTR111AIDRCR芯片转换为4-20mA电流输出
 *             - GP8101将PWM信号转换为电压信号
 *             - XTR111AIDRCR将电压信号转换为电流信号
 *             - 转换关系: 1V对应10mA (与bsp_dac.c中的dac_set_current_4_20ma相同)
 *             - 4mA对应0.4V，20mA对应2.0V，0mA对应0V
 * @retval     无
 */
void pwmdac_set_current_4_20ma(uint8_t channel, uint16_t curr)
{
    /* 判断通道是否为PWMDAC3或PWMDAC4 */
    if (channel != PWMDAC_CH3 && channel != PWMDAC_CH4)
    {
        return;  /* 只有PWMDAC3和PWMDAC4支持电流输出 */
    }
    
    /* 特殊情况处理：curr=0表示关闭输出 */
    if (curr == 0)
    {
        /* 设置为0V输出，对应0mA电流 */
        pwmdac_set_voltage(channel, 0);
        return;
    }
    
    /* 判断电流值是否在有效范围内 */
    if (curr < 4000) curr = 4000;  /* 标准4-20mA信号，最小值为4mA */
    if (curr > 20000) curr = 20000;
    
    /* 将电流值转换为电压值，关系为1V对应10mA */
    /* 计算方法: 电压(V) = 电流(mA) / 10 */
    double dac_volt = (double)curr / 10000.0;  /* 转换为V */
    
    /* 转换为pwmdac_set_voltage函数所需的参数格式(mV) */
    uint16_t dac_value = (uint16_t)(dac_volt * 1000);
    
    /* 调用基础的设置电压函数 */
    pwmdac_set_voltage(channel, dac_value);
}
